﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using ConsoleApplication1;
using Google.GData.Client;
using Google.GData.Spreadsheets;
using uk.org.aspellclark.todolistppc;

namespace SyncToolProgram
{
    public class Sync
    {
        private static readonly ArrayList AllWorksheets = new ArrayList();
        private static readonly List<SyncTask> Tasks = new List<SyncTask>();
        public double GetGooglePercentComplete;

        public double GetXmlPercentComplete;
        public double SetXmlPercentComplete;
        public double SetgooglePercentComplete;

        public List<SyncTask> GetTasksFromXml(string fileName)
        {
            try
            {
                var xmlTodoList = new XmlDocument();

                xmlTodoList.Load(fileName);

                if (xmlTodoList.GetElementsByTagName("TODOLIST").Count > 0)
                {
                    XmlNode xndList = xmlTodoList.GetElementsByTagName("TODOLIST").Item(0);


                    if (xndList != null)
                    {
                        if (xndList.HasChildNodes)
                        {
                            for (int i = 0; i < xndList.ChildNodes.Count; i++)
                            {
                                AppendTask(null, xndList.ChildNodes.Item(i));
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw new Exception("Get tasksFrom XML Failed");
            }
            GetXmlPercentComplete = 0;
            return Tasks;
        }

        public void SaveTasksToGoogleSpreadSheet(string userName, string password)
        {
            var service = new SpreadsheetsService("exampleCo-exampleApp-1");
            service.setUserCredentials(userName, password);

            // Demonstrate printing all spreadsheets and worksheets.
            PrintAllSpreadsheetsAndWorksheets(service);

            // Demonstrate retrieving the list feed for a single worksheet,
            // with the rows (ordered by position) reversed.
            const int userChoice = 1;
            var entry = AllWorksheets[userChoice] as WorksheetEntry;


            //Get SpreadsheetRows
            if (entry != null)
            {
                AtomLink listFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);
                var query = new ListQuery(listFeedLink.HRef.ToString());
                query.OrderByPosition = true;
                query.Reverse = true;
                ListFeed feed = service.Query(query);
                SetgooglePercentComplete = 0;
                double i = 1;
                foreach (SyncTask task in Tasks)
                {
                    SetgooglePercentComplete = i/Convert.ToDouble(Tasks.Count)*100;
                    i++;

                    bool taskExistsInSpreadsheet = false;

                    foreach (ListEntry googleRow in feed.Entries)
                    {
                        if (Convert.ToInt32(googleRow.Elements[0].Value) == task.TaskId)
                        {
                            var epoch = new DateTime(1970, 1, 1);
                            DateTime lastMod =
                                epoch.AddSeconds(double.Parse(googleRow.Elements[12].Value.Substring(0, 10)));
                            if (lastMod < task.LastMod)

                            {
                                foreach (ListEntry.Custom element in googleRow.Elements)
                                {
                                    if (element.LocalName == "parentid")
                                    {
                                        element.Value = task.ParentTaskId.ToString();
                                    }
                                }


                                googleRow.Elements[0].Value = task.TaskId.ToString();
                                googleRow.Elements[1].Value = task.Title;
                                googleRow.Elements[12].Value =
                                    (task.LastMod - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds + "000";
                                UpdateRow(googleRow);
                            }
                            taskExistsInSpreadsheet = true;
                        }
                    }
                    if (!taskExistsInSpreadsheet)
                    {
                        InsertRow(service, entry, task);

                        //service.Insert(entry.Feed,);
                    }
                }
            }
            SetgooglePercentComplete = 0;
        }

        public bool GetTasksFromGoogleSpreadsheet(string userName, string password)
        {
            var service = new SpreadsheetsService("exampleCo-exampleApp-1");
            service.setUserCredentials(userName, password);

            // Demonstrate printing all spreadsheets and worksheets.
            if (!PrintAllSpreadsheetsAndWorksheets(service))
            {
                GetGooglePercentComplete = 0;
                return false;
            }

            // Demonstrate retrieving the list feed for a single worksheet,
            // with the rows (ordered by position) reversed.
            int userChoice = 1;
            var entry = AllWorksheets[userChoice] as WorksheetEntry;
            Tasks.Clear();
            RetrieveListFeed(service, entry, true);
            GetGooglePercentComplete = 0;
            return true;
        }


        public void SaveTasksToXml(string fileName)
        {
            var oTodoList = new ToDoList();
            oTodoList.writeTodoListFile(fileName, Tasks);
            SetXmlPercentComplete = 0;
        }


        private void AppendTask(TodoListTreeNode tnParent, XmlNode xndTask)
        {
            TodoListTreeNode tnChild;
            SyncTask clsTask;

            if (xndTask.Name.ToUpper() == "TASK")
            {
                clsTask = new SyncTask();
                clsTask.readFromXml(xndTask);
                //if task already exists just update it else create a new task


                tnChild = new TodoListTreeNode(clsTask.Title);
                tnChild.TaskId = clsTask.TaskId;
                tnChild.Task = clsTask;
                if (tnParent != null)
                {
                    tnChild.ParentTaskId = tnParent.TaskId;
                    clsTask.ParentTaskId = tnParent.TaskId;
                }

                if (xndTask.HasChildNodes)
                {
                    for (int i = 0; i < xndTask.ChildNodes.Count; i++)
                    {
                        AppendTask(tnChild, xndTask.ChildNodes.Item(i));
                    }
                }
                tnChild.Checked = clsTask.Complete;
                AddSyncTask(clsTask);
            }
            else if (xndTask.Name.ToUpper() == "COMMENTS")
            {
                if (tnParent != null)
                {
                    clsTask = tnParent.Task;
                    clsTask.CommentsStr = xndTask.InnerText;
                    tnParent.Task = clsTask;
                }
            }
        }

        private static void AddSyncTask(SyncTask newTask)
        {
            if (Tasks.Where(t => t.TaskId == newTask.TaskId).Count() == 1)
            {
                SyncTask existingTask = Tasks.Single(t => t.TaskId == newTask.TaskId);
                if (newTask.LastMod > existingTask.LastMod)
                {
                    Tasks[Tasks.IndexOf(existingTask)] = newTask;
                }
            }
            else
            {
                Tasks.Add(newTask);
            }
        }

        #region Google Methods

        /// <summary>
        /// Prints a list of all worksheets in the specified spreadsheet.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the spreadsheet whose worksheets are to be retrieved</param>
        private void PrintAllWorksheets(SpreadsheetsService service,
                                        SpreadsheetEntry entry)
        {
            AtomLink link = entry.Links.FindService(GDataSpreadsheetsNameTable.WorksheetRel, null);

            var query = new WorksheetQuery(link.HRef.ToString());
            WorksheetFeed feed = service.Query(query);

            Console.WriteLine(@"Worksheets in " + entry.Title.Text + @":");
            foreach (WorksheetEntry worksheet in feed.Entries)
            {
                AllWorksheets.Add(worksheet);
                Console.WriteLine(@"  " + worksheet.Title.Text);
            }
            Console.WriteLine();
        }

        /// <summary>
        /// Prints a list of all the user's spreadsheets, and the
        /// list of worksheets that each spreadsheet contains.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        private bool PrintAllSpreadsheetsAndWorksheets(SpreadsheetsService service)
        {
            var query = new SpreadsheetQuery();
            try
            {
                SpreadsheetFeed feed = service.Query(query);

                Console.WriteLine(@"Your spreadsheets:");
                foreach (SpreadsheetEntry entry in feed.Entries)
                {
                    Console.WriteLine(@"Spreadsheet: {0}", entry.Title.Text);
                    PrintAllWorksheets(service, entry);
                }
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Retrieves and prints a list feed of the specified worksheet.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the worksheet to retrieve</param>
        /// <param name="reverseRows">true if the rows in the worksheet should
        /// be reversed when returned from the server</param>
        private void RetrieveListFeed(SpreadsheetsService service, WorksheetEntry entry,
                                      bool reverseRows)
        {
            AtomLink listFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);

            Console.WriteLine();
            Console.WriteLine(@"This worksheet's list feed URL is:");
            Console.WriteLine(listFeedLink.HRef);

            var query = new ListQuery(listFeedLink.HRef.ToString());
            if (reverseRows)
            {
                query.OrderByPosition = true;
                query.Reverse = true;
            }
            ListFeed feed = service.Query(query);

            Console.WriteLine();
            Console.WriteLine(@"Worksheet has {0} rows:", feed.Entries.Count);

            foreach (ListEntry worksheetRow in feed.Entries)
            {
                AddSyncTask(new SyncTask(worksheetRow));

                ListEntry.CustomElementCollection elements = worksheetRow.Elements;
                foreach (ListEntry.Custom element in elements)
                {
                    Console.Write(element.Value + @"\t");
                }
                Console.WriteLine();
            }
        }

        /// <summary>
        /// Inserts a new row in the specified worksheet.
        /// </summary>
        /// <param name="service">an authenticated SpreadsheetsService object</param>
        /// <param name="entry">the worksheet into which the row will be inserted</param>
        /// <param name="task"></param>
        /// <returns>the inserted ListEntry object, representing the new row</returns>
        private static void InsertRow(SpreadsheetsService service, WorksheetEntry entry, SyncTask task)
        {
            AtomLink listFeedLink = entry.Links.FindService(GDataSpreadsheetsNameTable.ListRel, null);

            var query = new ListQuery(listFeedLink.HRef.ToString());
            ListFeed feed = service.Query(query);

            var firstRow = feed.Entries[0] as ListEntry;
            var newRow = new ListEntry();

            Console.WriteLine();
            Console.WriteLine(@"Inserting a new row...");
            if (firstRow != null)
            {
                foreach (ListEntry.Custom element in firstRow.Elements)
                {
                    String elementValue = "";
                    if (element.LocalName == "title")
                    {
                        elementValue = task.Title;
                    }
                    else if (element.LocalName == "id")
                    {
                        elementValue = task.TaskId.ToString();
                    }
                    else if (element.LocalName == "parentid")
                    {
                        elementValue = task.ParentTaskId.ToString();
                    }
                    else if (element.LocalName == "priority")
                    {
                        elementValue = "1";
                    }
                    else if (element.LocalName == "modified")
                    {
                        //(Task.LastMod.ToUniversalTime().Ticks - 621355968000000000) / 10000000;
                        elementValue = ((task.LastMod.ToUniversalTime().Ticks - 621355968000000000)/10000000) + "000";
                    }
                    var curElement = new ListEntry.Custom();
                    curElement.LocalName = element.LocalName;
                    curElement.Value = elementValue;

                    newRow.Elements.Add(curElement);
                }
            }

            ListEntry insertedRow = feed.Insert(newRow);
            Console.WriteLine(@"Successfully inserted new row: ""{0}""",
                              insertedRow.Content.Content);

            return;
        }

        /// <summary>
        /// Updates the value of a cell in a single worksheet row.
        /// </summary>
        /// <param name="entry">the ListEntry representing the row to update</param>
        /// <returns>the updated ListEntry object</returns>
        private static void UpdateRow(ListEntry entry)
        {
            entry.Update();
        }

        #endregion
    }
}